import { AuthProvider, Message, Task, TaskArtifactUpdateEvent, TaskEventWatch, TaskFormDefineUpdateEvent, TaskStatusUpdateEvent } from "./types";

export class TaskWrap {
    private apiSchema: "http" | "https";
    private apiHost: string;
    private authProvider: AuthProvider;
    private taskId: string;
    private _task: Task | null = null;
    private eventWatch: TaskEventWatch;
    private _inWatch = true;

    constructor(taskId: string, apiSchema: "http" | "https", apiHost: string, authProvider: AuthProvider, eventWatch: TaskEventWatch) {
        this.apiSchema = apiSchema;
        this.apiHost = apiHost;
        this.authProvider = authProvider;
        this.taskId = taskId;
        this.eventWatch = eventWatch;
        this.loadTask();
        this.watchEvent();
    }

    get task() {
        return this._task;
    }

    startWatch() {
        this._inWatch = true;
        this.watchEvent();
    }

    stopWatch() {
        this._inWatch = false;
    }

    private getApiUrl(subPath: string): string {
        return `${this.apiSchema}://${this.apiHost}/api/v1${subPath}`;
    }

    //发送消息
    async addMessage(message: Message) {
        if (message.role != "user") {
            throw new Error("message role must be user");
        }
        if (this._task == null) {
            return;
        }
        const index = this._task.messages.findIndex(item => item.messageId == message.messageId);
        if (index != -1) {
            return;
        }
        this._task.messages.push(message);
        if (this._inWatch) {
            this.eventWatch.onEvnet(message);
        }
        //发送到服务器
        const url = this.getApiUrl(`/task/${this.taskId}/message`);
        const res = await fetch(url, {
            body: JSON.stringify(message),
            headers: {
                ...this.authProvider.getAuthHeader(),
                'Content-Type': 'application/json',
            },
            method: "POST",
            mode: "cors",
        });
        if (res.status != 200) {
            const errNsg = await res.text();
            throw new Error(errNsg);
        }
    }

    private async loadTask() {
        const url = this.getApiUrl(`/task/${this.taskId}/data?withMessage=true&withArtifact=true&withFormDefine=true`);
        const res = await fetch(url, {
            headers: this.authProvider.getAuthHeader(),
            method: "GET",
            mode: "cors",
        })
        if (res.status != 200) {
            const errNsg = await res.text();
            throw new Error(errNsg);
        }
        this._task = await res.json() as Task;
        if (this._inWatch) {
            this.eventWatch.onReady();
        }
    }

    private async watchEvent() {
        while (true) {
            if (!this._inWatch) {
                break;
            }
            await this.doWatchEvent();
        }
    }

    private async doWatchEvent() {
        const url = this.getApiUrl(`/task/${this.taskId}/event`);
        const response = await fetch(url, {
            headers: {
                ...this.authProvider.getAuthHeader(),
                "Accept": "text/event-stream",
            },
            method: "GET",
            mode: "cors",
        });
        if (!response.body) {
            throw new Error("SSE response body is undefined. Cannot read stream.");
        }
        const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();

        let buffer = "";
        let eventDataBuffer = "";

        while (true) {
            const { done, value } = await reader.read();
            if (done) {
                if (eventDataBuffer.trim()) {
                    this.processEventData(eventDataBuffer);
                }
                break;
            }

            buffer += value;
            let lineEndIndex;
            // Process all complete lines in the buffer
            while ((lineEndIndex = buffer.indexOf('\n')) >= 0) {
                const line = buffer.substring(0, lineEndIndex).trim(); // Get and trim the line
                buffer = buffer.substring(lineEndIndex + 1); // Remove processed line from buffer

                if (line === "") { // Empty line: signifies the end of an event
                    if (eventDataBuffer) { // If we have accumulated data for an event
                        this.processEventData(eventDataBuffer);
                        eventDataBuffer = ""; // Reset buffer for the next event
                    }
                } else if (line.startsWith("data:")) {
                    eventDataBuffer += line.substring(5).trimStart() + "\n"; // Append data (multi-line data is possible)
                } else if (line.startsWith(":")) {
                    // This is a comment line in SSE, ignore it.
                } else if (line.includes(":")) {
                    // Other SSE fields like 'event:', 'id:', 'retry:'.
                    // The A2A spec primarily focuses on the 'data' field for JSON-RPC payloads.
                    // For now, we don't specifically handle these other SSE fields unless required by spec.
                }
            }
        }
    }

    private processEventData(jsonData: string) {
        if (jsonData.trim() == "") {
            return;
        }
        const ev = JSON.parse(jsonData) as Message | TaskStatusUpdateEvent | TaskArtifactUpdateEvent | TaskFormDefineUpdateEvent;
        if (this._task != null) {
            if (ev.kind == "message") {
                const message = ev as Message;
                const index = this._task.messages.findIndex(item => item.messageId == message.messageId);
                if (index == -1) {
                    this._task.messages.push(message);
                } else {
                    this._task.messages[index] = message;
                }
            } else if (ev.kind == "status-update") {
                const statusEv = ev as TaskStatusUpdateEvent;
                this._task.status = statusEv.status;
            } else if (ev.kind == "artifact-update") {
                const artifactEv = ev as TaskArtifactUpdateEvent;
                const index = this._task.artifacts.findIndex(item => item.artifactId == artifactEv.artifact.artifactId);
                if (index == -1) {
                    this._task.artifacts.push(artifactEv.artifact);
                } else {
                    this._task.artifacts[index] = artifactEv.artifact;
                }
            } else if (ev.kind == "formdefine-update") {
                const fromDefineEv = ev as TaskFormDefineUpdateEvent;
                const index = this._task.formDefines.findIndex(item => item.formDefineId == fromDefineEv.formDefine.formDefineId);
                if (index == -1) {
                    this._task.formDefines.push(fromDefineEv.formDefine);
                } else {
                    this._task.formDefines[index] = fromDefineEv.formDefine;
                }
            }
        }
        this.eventWatch.onEvnet(ev);
    }
}